package com.xinqi.modules.course.course.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xinqi.common.base.enums.YesNoEnum;
import com.xinqi.common.base.error.CommonErrors;
import com.xinqi.common.base.response.PageInfo;
import com.xinqi.common.base.response.Param;
import com.xinqi.common.base.response.R;
import com.xinqi.common.base.user.Account;
import com.xinqi.common.base.utils.lambda.Lambdas;
import com.xinqi.common.base.utils.spring.Spring;
import com.xinqi.common.core.constant.CacheConstants;
import com.xinqi.common.core.enums.ClassRoleEnum;
import com.xinqi.common.core.utils.Md5HashUtil;
import com.xinqi.common.core.utils.MessageUtils;
import com.xinqi.common.mybatis.data.service.impl.BaseServiceImpl;
import com.xinqi.common.mybatis.utils.Pages;
import com.xinqi.common.mybatis.utils.Queries;
import com.xinqi.common.redis.utils.RedisUtils;
import com.xinqi.common.satoken.utils.LoginHelper;
import com.xinqi.modules.course.clazz.convert.ClassConvert;
import com.xinqi.modules.course.clazz.convert.ClassStudentConvert;
import com.xinqi.modules.course.clazz.domain.ClassEntity;
import com.xinqi.modules.course.clazz.domain.ClassStudentEntity;
import com.xinqi.modules.course.clazz.dto.req.ClassCreateDTO;
import com.xinqi.modules.course.clazz.dto.req.ClassUpdateDTO;
import com.xinqi.modules.course.clazz.dto.req.ClassUpdateNewDTO;
import com.xinqi.modules.course.clazz.dto.rsp.ClassResultDTO;
import com.xinqi.modules.course.clazz.dto.rsp.ClassStudentResultDTO;
import com.xinqi.modules.course.clazz.dto.rsp.ClassTeacherResultDTO;
import com.xinqi.modules.course.clazz.service.ClassService;
import com.xinqi.modules.course.clazz.service.ClassStudentService;
import com.xinqi.modules.course.clazz.service.impl.ClassMqService;
import com.xinqi.modules.course.course.constant.CourseConstants;
import com.xinqi.modules.course.course.convert.CourseConvert;
import com.xinqi.modules.course.course.convert.CourseTransferConvert;
import com.xinqi.modules.course.course.domain.CourseEntity;
import com.xinqi.modules.course.course.dto.req.*;
import com.xinqi.modules.course.course.dto.rsp.CourseResultDTO;
import com.xinqi.modules.course.course.dto.rsp.CourseResultNewDTO;
import com.xinqi.modules.course.course.enums.ClassStatusEnum;
import com.xinqi.modules.course.course.enums.CourseArchiveStatusEnum;
import com.xinqi.modules.course.course.enums.CourseStatusEnum;
import com.xinqi.modules.course.course.mapper.CourseMapper;
import com.xinqi.modules.course.course.service.CourseService;
import com.xinqi.modules.course.course.thread.CopyCourseContentService;
import com.xinqi.modules.exam.student.client.CourseExamPaperStudentClient;
import com.xinqi.modules.exam.student.dto.req.CourseExamPaperStudentBatchDelDTO;
import com.xinqi.modules.sys.textbook.client.CloudTextbookInfoClient;
import com.xinqi.modules.sys.textbook.dto.rsp.CloudTextbookInfoResultDTO;
import com.xinqi.modules.user.user.client.UserClient;
import com.xinqi.modules.user.user.dto.rsp.UserPasswordResultDTO;
import com.xinqi.modules.user.user.dto.rsp.UserResultDTO;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 课程表服务层实现
 *
 * @author: system
 * @date: 2023/01/17
 */
@Service("courseService")
@RequiredArgsConstructor
public class CourseServiceImpl extends BaseServiceImpl<CourseMapper, CourseEntity> implements CourseService {

    private final CourseConvert courseConvert;
    private final ClassConvert classConvert;
    private final ClassService classService;
    private final ClassStudentService classStudentService;
    private final ClassStudentConvert classStudentConvert;
    private final CourseMapper courseMapper;
    private final CourseTransferConvert courseTransferConvert;
    private final UserClient userClient;

    private final CourseExamPaperStudentClient examPaperStudentClient;

    private final CloudTextbookInfoClient cloudTextbookInfoClient;
    private final CopyCourseContentService copyCourseContentService;

    @Lazy
    private final ClassMqService classMqService;

    /**
     * 分页查询 课程表数据
     */
    @Override
    @DS("slaver")
    public PageInfo<CourseResultDTO> page(Param pageable, CourseQueryDTO query) {
        IPage<CourseEntity> page = super.page(Pages.page(pageable), wrapper(query));
        return Pages.convert(page, courseConvert::convert);
    }

    /**
     * 查询 课程表数据
     */
    @Override
    public List<CourseResultDTO> find(CourseQueryDTO query) {
        return list(wrapper(query)).stream().map(courseConvert::convert).collect(Collectors.toList());
    }

    private LambdaQueryWrapper<CourseEntity> wrapper(CourseQueryDTO query) {
        LambdaQueryWrapper<CourseEntity> wrapper = Queries.lambda();
        Queries.accept(query.getId(), v -> wrapper.eq(CourseEntity::getId, v));
        Queries.accept(query.getCourseName(), v -> wrapper.like(CourseEntity::getCourseName, v));
        Queries.accept(query.getUserId(), v -> wrapper.eq(CourseEntity::getUserId, v));
        return wrapper;
    }

    /**
     * 根据id查询 课程表数据
     */
    @Override
    public CourseResultDTO findById(Long id) {
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(id), "ID不能为空");
        return Optional.ofNullable(courseMapper.selectById(id)).map(courseConvert::convert).orElse(null);
    }

    /**
     * 根据id查询 课程表数据--变更
     */
    @Override
    public CourseResultNewDTO findDetailById(Long id) {
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(id), "ID不能为空");
        CourseResultDTO result = Optional.ofNullable(courseMapper.selectById(id)).map(courseConvert::convert).orElse(null);

        // 生成新的课程内容数据
        CourseResultNewDTO newDTO = courseConvert.convert(result);
        List<ClassResultDTO> classList = classService.findByCourseId(newDTO.getId());
        newDTO.setTeachingNode(classList.get(0).getTeachingNode());
        newDTO.setAuditMethod(classList.get(0).getAuditMethod());
        newDTO.setAuditInfo(classList.get(0).getAuditInfo());
        newDTO.setClassList(classConvert.convert(classList));
        return newDTO;
    }

    /**
     * 新增 课程表数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long insert(CourseCreateDTO dto) {

        CommonErrors.BAD_REQUEST.check(CollectionUtils.isNotEmpty(dto.getClassList()), "请添加班级");

        int createdCourseCount = Math.toIntExact(findCourseCountByUserId(LoginHelper.getUserId()));
        CommonErrors.BAD_REQUEST.check(createdCourseCount < CourseConstants.Course.CREATED_COUNT, "每个用户最多只能创建50个课程");
        CommonErrors.BAD_REQUEST.check(dto.getClassList().size() <= CourseConstants.ClassCount.COURSE_CLASS_COUNT, "一个课程下最多只能有5个班级");
        CommonErrors.BAD_REQUEST.check(dto.getClassList().size() <= CourseConstants.ClassCount.COURSE_CLASS_COUNT, "一个课程下最多只能有5个班级");

        CourseEntity entity = courseConvert.create(dto);
        // 设置课程拥有人
        entity.setUserId(LoginHelper.getUserId());
        //设置状态
        entity.setStatus(CourseStatusEnum.NORMAL);
        entity.setArchive(CourseArchiveStatusEnum.NORMAL);

        // 保存课程信息
        if (Objects.nonNull(dto.getCloudTextbookId())) {
            this.setCloudTextbookAttribute(dto.getCloudTextbookId(), entity);
        }

        super.save(entity);

        // 设置课程ID
        dto.getClassList().forEach(v -> v.setCourseId(entity.getId()));

        // 添加班级列表
        List<Long> classIdList = classService.insert(dto.getClassList());

        copyCourseContentService.saveCloudTextbook(entity.getId(), classIdList, dto.getCloudTextbookId());

        return entity.getId();
    }


    /**
     * 新增 课程表数据--变更
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long insertCourse(CourseCreateNewDTO dto) {

        CommonErrors.BAD_REQUEST.check(CollectionUtils.isNotEmpty(dto.getClassList()), MessageUtils.message("course.class.length.not.blank"));

        int createdCourseCount = Math.toIntExact(findCourseCountByUserId(LoginHelper.getUserId()));
        CommonErrors.BAD_REQUEST.check(createdCourseCount < CourseConstants.Course.CREATED_COUNT, MessageUtils.message("course.max.valid"));
        CommonErrors.BAD_REQUEST.check(dto.getClassList().size() <= CourseConstants.ClassCount.COURSE_CLASS_COUNT, MessageUtils.message("course.class.max.valid"));

        CourseEntity entity = courseConvert.create(dto);

        // 设置课程拥有人
        entity.setUserId(LoginHelper.getUserId());

        //设置状态
        entity.setStatus(CourseStatusEnum.NORMAL);
        entity.setArchive(CourseArchiveStatusEnum.NORMAL);

        // 保存课程信息
        if (Objects.nonNull(dto.getCloudTextbookId())) {
            this.setCloudTextbookAttribute(dto.getCloudTextbookId(), entity);
        }

        // 保存用户信息
        super.save(entity);

        // 初始化班级数据 添加班级列表
        List<ClassCreateDTO> list = dto.getClassList().stream().map(v -> {
            ClassCreateDTO classCreate = new ClassCreateDTO();
            classCreate.setCourseId(entity.getId());
            classCreate.setName(v.getName());
            classCreate.setTeachingNode(dto.getTeachingNode());
            classCreate.setAuditMethod(dto.getAuditMethod());
            classCreate.setAuditInfo(dto.getAuditInfo());
            return classCreate;
        }).collect(Collectors.toList());
        List<Long> classIdList = classService.insert(list);

        copyCourseContentService.saveCloudTextbook(entity.getId(), classIdList, dto.getCloudTextbookId());

        return entity.getId();
    }

    /**
     * 根据id修改 课程表数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateById(Account user, CourseUpdateDTO dto) {

        List<ClassUpdateDTO> classList = dto.getClassList();
        CommonErrors.BAD_REQUEST.check(CollectionUtils.isNotEmpty(classList), MessageUtils.message("course.class.length.not.blank"));
        CommonErrors.BAD_REQUEST.check(dto.getClassList().size() <= CourseConstants.ClassCount.COURSE_CLASS_COUNT, MessageUtils.message("course.max.valid"));

        CourseEntity old = Optional.ofNullable(getById(dto.getId())).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException(MessageUtils.message("course.data.exist.valid")));

        //课程名称只可以更改20次
        if (!Objects.equals(old.getCourseName(), dto.getCourseName())) {
            CommonErrors.BAD_REQUEST.check(old.getNameEditCount() + 1 <= CourseConstants.Course.EDITABLE_COUNT, MessageUtils.message("course.name.update.max.valid"));
            dto.setNameEditCount(old.getNameEditCount() + 1);
            dto.setIsNameEditable(YesNoEnum.NO.ordinal());
        }

        CourseEntity entity = courseConvert.update(dto);

        if (Objects.nonNull(dto.getCloudTextbookId()) && Objects.isNull(old.getCloudTextbookId())) {
            this.setCloudTextbookAttribute(dto.getCloudTextbookId(), entity);
        }
        boolean b = updateById(entity);

        // 修改班级信息
        classList.forEach(v -> v.setCourseId(old.getId()));
        classService.update(user, entity.getId(), classList);

        //保存云课本
        this.saveCloudTextbook(old, classList.stream().map(ClassUpdateDTO::getId).collect(Collectors.toList()), dto);

        return b;
    }

    /**
     * 根据id修改 课程表数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateCourseById(Account user, CourseUpdateNewDTO dto) {

        List<ClassUpdateNewDTO> classList = dto.getClassList();

        CommonErrors.BAD_REQUEST.check(CollectionUtils.isNotEmpty(classList), MessageUtils.message("course.class.length.not.blank"));
        CommonErrors.BAD_REQUEST.check(dto.getClassList().size() <= CourseConstants.ClassCount.COURSE_CLASS_COUNT, MessageUtils.message("course.class.max.valid"));
        CourseEntity old = Optional.ofNullable(getById(dto.getId())).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException(MessageUtils.message("course.data.exist.valid")));

        //课程名称只可以更改20次
        if (!Objects.equals(old.getCourseName(), dto.getCourseName())) {
            CommonErrors.BAD_REQUEST.check(old.getNameEditCount() + 1 <= CourseConstants.Course.EDITABLE_COUNT, MessageUtils.message("course.name.update.max.valid"));
            dto.setNameEditCount(old.getNameEditCount() + 1);
            dto.setIsNameEditable(YesNoEnum.NO.ordinal());
        }

        // 转换
        CourseEntity entity = courseConvert.update(dto);

        if (Objects.nonNull(dto.getCloudTextbookId()) && Objects.isNull(old.getCloudTextbookId())) {
            this.setCloudTextbookAttribute(dto.getCloudTextbookId(), entity);
        }

        boolean b = updateById(entity);

        // 校验班级名称是否重复
        this.checkClassNames(dto.getClassList());
        // 修改班级信息
        List<ClassUpdateDTO> list = dto.getClassList().stream().map(v -> {
            ClassUpdateDTO updateDto = new ClassUpdateDTO();
            updateDto.setCourseId(old.getId());
            updateDto.setId(v.getId());
            updateDto.setName(v.getName());
            updateDto.setTeachingNode(dto.getTeachingNode());
            updateDto.setAuditMethod(dto.getAuditMethod());
            updateDto.setAuditInfo(dto.getAuditInfo());
            return updateDto;
        }).collect(Collectors.toList());
        // 获取新添加班级
        List<Long> newIds = classService.updateNew(user, entity.getId(), list);

        List<Long> ids;
        if (Objects.nonNull(old.getCloudTextbookId()) && old.getCloudTextbookId() > 0L) {
            ids = newIds;
        } else {
            ids = list.stream().map(ClassUpdateDTO::getId).collect(Collectors.toList());
        }

        if (Objects.nonNull(ids) && ids.size() > 0) {
            copyCourseContentService.saveCloudTextbook(entity.getId(), ids, dto.getCloudTextbookId());
        }
        return b;
    }

    /**
     * 校验班级名称是否重复
     *
     * @Author dzy
     */
    private void checkClassNames(List<ClassUpdateNewDTO> list) {
        List<String> nameList = list.stream().map(ClassUpdateNewDTO::getName).distinct().collect(Collectors.toList());
        if (nameList.size() != list.size()) {
            CommonErrors.BAD_REQUEST.asException("该课程下的班级名称有重复");
        }
    }

    /**
     * 根据id删除 课程表数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteById(Long id) {
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(id), "ID不能为空");
        return removeById(id);
    }

    /**
     * 根据id删除 课程表数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteById(CourseDeleteDTO dto) {
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(dto.getId()), "ID不能为空");
        return deleteById(dto.getId());
    }

    @Override
    public Boolean isExistsByName(String name, Long id) {
        CommonErrors.BAD_REQUEST.check(StringUtils.isNotBlank(name), "课程名称不能为空");
        LambdaQueryWrapper<CourseEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CourseEntity::getCourseName, name);
        if (id != null) {
            wrapper.ne(CourseEntity::getId, id);
        }
        return count(wrapper) > 0;
    }

    @Override
    public CourseResultDTO findOne(CourseQueryDTO query) {
        CourseEntity entity = Optional.ofNullable(getOne(wrapper(query))).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("数据不存在"));

        return courseConvert.convert(entity);
    }

    /**
     * 名称是否可编辑，这个字段存在了扩展字段中；课程名称是否可编辑，业务上只能编辑一次，
     *
     * @param courseId 课程id
     * @return ture-可编辑，false-不可编辑
     */
    @Override
    public Boolean isNameEditable(Long courseId) {
        CourseEntity old = Optional.ofNullable(getById(courseId)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("数据不存在"));
        int isNameEditable = old.getIsNameEditable();
        return Objects.equals(isNameEditable, YesNoEnum.YES.ordinal());
    }

    /**
     * 解散逻辑：若选择了课程下所有的班级，班级、学生、对应的教学数据都要删除，另外还需要删除该课程，否则不删除课程
     *
     * @param courseId    要解散的课程id
     * @param classIdList 要解散的班级id列表
     * @return Boolean
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean dissolve(Long courseId, String smsCode, String mobile, List<Long> classIdList, Account user) {
        CommonErrors.BAD_REQUEST.check(CollectionUtils.isNotEmpty(classIdList), "请选择班级");
        CourseEntity old = Optional.ofNullable(this.baseMapper.selectById(courseId)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("课程不存在"));
        CommonErrors.BAD_REQUEST.check(Objects.equals(user.getId(), old.getUserId()), MessageUtils.message("course.comment.is.not.permissions"));

        String code = RedisUtils.getCacheObject(CacheConstants.CAPTCHA_CODE_KEY + mobile);

        // TODO 上线需注释掉万能验证码 666666
        // if (Objects.isNull(code)) { code = "666666"; }
        CommonErrors.BAD_REQUEST.check(Objects.equals(smsCode, code), "验证码错误");
        // 验证通过删除 验证码
        RedisUtils.deleteObject(CacheConstants.CAPTCHA_CODE_KEY + mobile);

        List<ClassResultDTO> allClassList = classService.findByCourseId(courseId);

        //批量删除课程及课程下的教学数据
        classService.batchDeleteClassAndSubInfo(user, courseId, classIdList);

        allClassList.removeIf(a -> classIdList.contains(a.getId()));

        if (CollectionUtils.isEmpty(allClassList)) {
            //删除课程
            deleteById(courseId);
        }

        return true;
    }

    /**
     * 归档课程，包括归档自己和归档全班，如果是归档自己，则只有自己不可见，如果是归档全班，该课程全班不可见
     * 我创建的课程，我可以归档全班，我加入的课程只可归档自己；归档后课程结束置顶
     *
     * @param courseId 课程id
     * @return Boolean
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean archiveAll(Long courseId) {
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(courseId), "课程ID不能为空");
        CourseEntity old = Optional.ofNullable(getById(courseId)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("要归档的课程不存在"));
        Long userId = LoginHelper.getUserId();

        //归档课程
        CommonErrors.BAD_REQUEST.check(Objects.equals(userId, old.getUserId()), "你不能归档该课程");
        old.setStatus(CourseStatusEnum.ARCHIVE);
        old.setArchive(CourseArchiveStatusEnum.ARCHIVE_ALL);
        updateById(old);

        //停用加课码
        List<ClassResultDTO> classList = classService.findByCourseId(courseId);
        classList.forEach(c -> {
            c.setStatus(ClassStatusEnum.DISABLE.getCode());
            classService.closeCode(c.getId());
        });

        //归档全课程下的学生
        List<ClassStudentResultDTO> studentList = classStudentService.findAllRolesStudentByCourseId(courseId);
        CommonErrors.BAD_REQUEST.check(CollectionUtils.isNotEmpty(studentList), "学生数据不存在");
        studentList.forEach(s -> {
            s.setIsArchive(YesNoEnum.YES.ordinal());
            classStudentService.updateById(classStudentConvert.convert(s));
        });

        return true;
    }

    /**
     * 归档恢复
     *
     * @param courseId 课程id
     * @return Boolean
     */
    @Override
    public Boolean recoverSelf(Long courseId) {
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(courseId), "课程ID不能为空");
        CourseEntity old = Optional.ofNullable(getById(courseId)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("要归档的课程不存在"));

        Long studentId = LoginHelper.getUserId();
        List<ClassStudentResultDTO> studentList = classStudentService.findByCourseIdAndStudentId(courseId, studentId);
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(studentList), "数据不存在");

        //归档学生，老师也是属于班级中的学生
        studentList.forEach(s -> {
            s.setIsArchive(YesNoEnum.NO.ordinal());
            s.setIsTop(YesNoEnum.NO.ordinal());
        });

        return classStudentService.saveBatch(studentList.stream().map(classStudentConvert::convertEntity).collect(Collectors.toList()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean recoverAll(Long courseId) {
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(courseId), "课程ID不能为空");
        CourseEntity old = Optional.ofNullable(getById(courseId)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("要恢复的归档课程不存在"));

        //恢复课程
        CommonErrors.BAD_REQUEST.check(Objects.equals(LoginHelper.getUserId(), old.getUserId()), "你不能恢复该课程");
        old.setStatus(CourseStatusEnum.NORMAL);
        old.setArchive(CourseArchiveStatusEnum.NORMAL);
        old.setIsTop(YesNoEnum.NO.ordinal());
        updateById(old);

        //启用加课码
        List<ClassResultDTO> classList = classService.findByCourseId(courseId);
        classList.forEach(c -> {
            if (Objects.equals(ClassStatusEnum.DISABLE.getCode(), c.getStatus())) {
                c.setStatus(ClassStatusEnum.NORMAL.getCode());
                classService.openCode(c.getId());
            }
        });

        //恢复全课程下的学生
        List<ClassStudentResultDTO> studentList = classStudentService.findAllRolesStudentByCourseId(courseId);
        CommonErrors.BAD_REQUEST.check(CollectionUtils.isNotEmpty(studentList), "学生数据不存在");
        studentList.forEach(s -> {
            s.setIsArchive(YesNoEnum.NO.ordinal());
            s.setIsTop(YesNoEnum.NO.ordinal());
            classStudentService.updateById(classStudentConvert.convert(s));
        });
        return true;
    }

    /**
     * 我创建的归档列表中：课程包含两种状态 1-正常，2-归档。
     * 恢复归档的时候前端需要根据课程的status来决定调用的是恢复自己还是恢复全班，这两个逻辑是不一样的
     *
     * @param page
     * @return PageInfo<CourseResultDTO>
     */
    @Override
    @DS("slaver")
    public PageInfo<CourseResultDTO> createdArchivePageList(Page<CourseEntity> page) {
        Long userId = LoginHelper.getUserId();
        CourseQueryDTO query = new CourseQueryDTO();
        query.setUserId(userId);
        query.setIsTeacher(YesNoEnum.YES.ordinal());
        //学生归档状态-已归档
        query.setIsArchive(YesNoEnum.YES.ordinal());
        IPage<CourseResultDTO> pageList = courseMapper.createdArchivePageList(page, query);

        //设置班级
        Lambdas.each(pageList.getRecords(), courseConvert::renderArchived);
        return Pages.convert(pageList);
    }

    @Override
    @DS("slaver")
    public PageInfo<CourseResultDTO> joinedArchivePageList(Page<CourseEntity> page) {
        Long userId = LoginHelper.getUserId();
        CourseQueryDTO query = new CourseQueryDTO();
        query.setUserId(userId);
        query.setIsTeacher(YesNoEnum.NO.ordinal());
        //学生归档状态-已归档
        query.setIsArchive(YesNoEnum.YES.ordinal());
        IPage<CourseResultDTO> pageList = courseMapper.joinedArchivePageList(page, query);

        //设置班级
        Lambdas.each(pageList.getRecords(), courseConvert::renderArchived);
        return Pages.convert(pageList);
    }

    @Override
    public List<CourseResultDTO> findMyJoinedList() {
        List<CourseResultDTO> list = courseMapper.findMyJoinedList(this.setJoinedQueryProps());
        Lambdas.each(list, courseConvert::renderJoined);
        return list;
    }

    private CourseQueryDTO setJoinedQueryProps() {
        CourseQueryDTO query = new CourseQueryDTO();
        query.setJoinUserId(LoginHelper.getUserId());
        query.setStatus(CourseStatusEnum.NORMAL.getCode());
        query.setIsTeacher(YesNoEnum.NO.ordinal());
        query.setIsArchive(YesNoEnum.NO.ordinal());
        return query;
    }

    private CourseQueryDTO setCreatedQueryProps() {
        CourseQueryDTO query = new CourseQueryDTO();
        query.setUserId(LoginHelper.getUserId());
        query.setIsTeacher(YesNoEnum.YES.ordinal());
        query.setStatus(CourseStatusEnum.NORMAL.getCode());
        query.setIsArchive(YesNoEnum.NO.ordinal());
        return query;
    }

    @Override
    public List<CourseResultDTO> findMyCreatedList() {
        List<CourseResultDTO> list = courseMapper.findMyCreatedList(this.setCreatedQueryProps());
        Lambdas.each(list, courseConvert::renderCreated);
        return list;
    }

    /**
     * 我创建的课程为入口进入
     *
     * @param courseId 课程id
     * @return
     */
    @Override
    public CourseResultDTO findMyCreatedById(Long courseId) {
        CourseResultDTO course = this.findById(courseId);
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(course), "课程信息不存在");

        //设置班级
        List<ClassResultDTO> classList = classService.findByCourseIdAndIsTeacher(courseId, null, LoginHelper.getUserId(), YesNoEnum.YES.ordinal(), YesNoEnum.NO.ordinal());
        course.setClassList(classList);

        //设置学生，需要排除老师
        Lambdas.each(classList, this::render);
        return course;
    }

    public void render(ClassResultDTO dto) {
        List<ClassStudentResultDTO> studentList = classStudentService.findStudentExcludeTeacher(dto.getId());
        dto.setStudentList(studentList);
    }

    /**
     * 我加入的 课程、班级、学生数据结构，课程下一定会有班级，班级下不一定有学生(老师不能算学生)
     *
     * @param courseId 课程id
     * @return CourseResultDTO
     */
    @Override
    public CourseResultDTO findMyJoinedById(Long courseId) {
        CourseResultDTO course = this.findById(courseId);
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(course), "课程信息不存在");

        List<String> classRoles = new ArrayList<>();
        classRoles.add(ClassRoleEnum.REPRESENTATIVE.getCode());
        classRoles.add(ClassRoleEnum.STUDENT.getCode());
        //设置班级
        List<ClassResultDTO> classList = classService.findByCourseIdAndRoles(courseId, null, LoginHelper.getUserId(), classRoles, YesNoEnum.NO.ordinal());
        course.setClassList(classList);

        //设置学生，需要排除老师
        Lambdas.each(classList, this::render);
        return course;
    }

    /**
     * 账号注销（课程板块变动）
     *
     * @param userId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean accountCancel(Long userId) {
        Account account = new Account();
        try {
            account = LoginHelper.getXqLoginUser(userId);
        } catch (Exception e) {
            UserResultDTO userResult = R.as(userClient.findById(userId));
            account = LoginHelper.user2Account(userResult);
        }
        //删除注销账号所加入的班级与小组数据
        classStudentService.deleteByStudentId(account, userId);

        List<Long> courseIdList = findCourseIdListByUserId(userId);
        //删除班级
        if (CollectionUtils.isNotEmpty(courseIdList)) {
            for (Long courseId : courseIdList) {
                classService.deleteByCourseId(account, courseId);
            }
        }

        // 删除测评
        CourseExamPaperStudentBatchDelDTO studentDel = new CourseExamPaperStudentBatchDelDTO();
        List<Long> userIds = new ArrayList<>();
        userIds.add(userId);
        studentDel.setUserIdList(userIds);
        examPaperStudentClient.batchDelUser(studentDel);

        //删除课程
        return removeBatchByIds(courseIdList);
    }

    private List<Long> findCourseIdListByUserId(Long userId) {
        CourseQueryDTO query = new CourseQueryDTO();
        query.setUserId(userId);
        return find(query).stream().map(CourseResultDTO::getId).collect(Collectors.toList());
    }

    @Override
    public CourseResultDTO findMyCreatedByClassId(Long classId) {

        CourseResultDTO course = this.findByClassIdAndLoginId(classId, YesNoEnum.YES.ordinal());
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(course), "课程信息不存在");

        //设置班级
        List<ClassResultDTO> classList = classService.findByCourseIdAndIsTeacher(course.getId(), classId, LoginHelper.getUserId(), YesNoEnum.YES.ordinal(), YesNoEnum.NO.ordinal());
        course.setClassList(classList);

        //设置老师
        Lambdas.each(classList, this::renderTeacher);

        //设置学生，需要排除老师
        Lambdas.each(classList, this::render);
        return course;
    }

    @Override
    public CourseResultDTO findMyJoinedByClassId(Long classId) {
        CourseResultDTO course = this.findByClassIdAndLoginId(classId, YesNoEnum.NO.ordinal());
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(course), "课程信息不存在");

        List<String> classRoles = new ArrayList<>();
        classRoles.add(ClassRoleEnum.REPRESENTATIVE.getCode());
        classRoles.add(ClassRoleEnum.STUDENT.getCode());
        //设置班级
        List<ClassResultDTO> classList = classService.findByCourseIdAndRoles(course.getId(), classId, LoginHelper.getUserId(), classRoles, YesNoEnum.NO.ordinal());
        course.setClassList(classList);

        //设置老师
        Lambdas.each(classList, this::renderTeacher);

        //设置学生，需要排除老师
        Lambdas.each(classList, this::render);
        return course;
    }

    @Override
    @DS("slaver")
    public PageInfo<CourseResultDTO> findMyJoinedPage(Param pageable) {
        IPage<CourseResultDTO> page = courseMapper.findMyJoinedPage(Pages.page(pageable), this.setJoinedQueryProps());
        Lambdas.each(page.getRecords(), courseConvert::renderJoined);
        return Pages.convert(page);
    }

    @Override
    @DS("slaver")
    public PageInfo<CourseResultDTO> findMyCreatedPage(Param pageable) {
        IPage<CourseResultDTO> page = courseMapper.findMyCreatedPage(Pages.page(pageable), this.setCreatedQueryProps());
        Lambdas.each(page.getRecords(), courseConvert::renderCreated);
        return Pages.convert(page);
    }

    /**
     * 课程转让，实际上是班级复制
     * 分两种情况，第一种：完全转让，把一个课程下的班级全部转让给另外一个人，并且这个人不是该班级中的人。1.将课程的拥有者id替换，2.班级的拥有者id更换，3.将班级学生表中老师的studentId替换为接收者的userId，其他不变。
     * 第二种情况 部分转让，1.部分转让需要新增课程，新增课程的拥有者为接收者id，2.将要转让的班级对应的课程id替换为新的课程id，要转让的班级拥有者id替换为接收人id，3.要转让的班级中，对应的班级学生表中的老师的studentId替换为接收者的userId，4.要转让的班级中如果存在小组，则将小组对应的课程id替换。
     * 第三种情况：分多次部分转让，前几次都按部分转让处理，最后一次按全部转让处理。
     * 总结起来有四步：1.课程拥有者的变动、2.班级拥有者的变动和所属课程的变动、3.班级老师的变动、4.班级小组的变动。由于是转让给班级外的人，所以，不存在学生老师身份转换的情况，也不用考虑班级学生和小组学生的情况。
     *
     * @param dto+
     * @return Boolean
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean transfer(CourseTransferDTO dto) {
        CommonErrors.BAD_REQUEST.check(CollectionUtils.isNotEmpty(dto.getClassIdList()), "请选择转让班级");

        //1.查询手机号对应的人是否存在
        UserResultDTO receiver = R.as(userClient.findByMobile(dto.getMobile()));
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(receiver), "未找到对应的用户");
        CommonErrors.BAD_REQUEST.check(!Objects.equals(receiver.getId(), LoginHelper.getUserId()), "不能将课程转让给自己");

        //2.检验接收人手机号验证码
        String code = RedisUtils.getCacheObject(CacheConstants.CAPTCHA_CODE_KEY + dto.getMobile());
        CommonErrors.BAD_REQUEST.check(Objects.equals(dto.getSmsCode(), code), "新手机号验证码不正确");
        // 校验成功删除验证码
        RedisUtils.deleteObject(CacheConstants.CAPTCHA_CODE_KEY + dto.getMobile());

        //3.校验课程
        CourseEntity old = Optional.ofNullable(this.baseMapper.selectById(dto.getCourseId()))
            .orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("要转让的课程不存在"));
        CommonErrors.BAD_REQUEST.check(Objects.equals(dto.getOperationUserId(), old.getUserId()), MessageUtils.message("course.comment.is.not.permissions"));
        List<ClassStudentResultDTO> existStudentList = classStudentService.findAllRolesStudentByCourseId(dto.getCourseId());
        if (CollectionUtils.isNotEmpty(existStudentList)) {
            Map<Long, ClassStudentResultDTO> existStudentMap = existStudentList.stream().collect(Collectors.toMap(ClassStudentResultDTO::getStudentId, v -> v));
            if (existStudentMap.containsKey(receiver.getId())) {
                ClassStudentResultDTO existStudent = existStudentMap.get(receiver.getId());
                //可以转移给助教
                if (!Objects.equals(ClassRoleEnum.ASSISTANT.getCode(), existStudent.getClassRole())) {
                    throw CommonErrors.BAD_REQUEST.asException("不能将课程转让给课程下的学生或老师自己");
                }
            }
        }

        //4.检查是否为全量转让
        List<ClassResultDTO> allClassList = classService.findByCourseId(dto.getCourseId());
        boolean allMatch = allClassList.stream().allMatch(existClass -> dto.getClassIdList().contains(existClass.getId()));

        //第一种情况：全部转让
        if (allMatch) {
            //A.将课程的拥有者替换
            old.setUserId(receiver.getId());
            this.baseMapper.updateById(old);

            //B.将班级的userId替换
            List<ClassEntity> classList = classService.findByIdList(dto.getClassIdList());
            classList.forEach(c -> c.setUserId(receiver.getId()));
            classService.updateBatch(classList);

            //C.替换班级中的老师
            List<ClassStudentEntity> teacherList = classStudentService.findTeacherListByClassIds(dto.getClassIdList());
            classStudentService.transferTeacherList(receiver, teacherList);

            Spring.committed(() -> {
                //D.班级群转让
                dto.getClassIdList().forEach(classId -> classMqService.courseTransfer(classId, receiver.getId()));
            });

            //第二种情况：部分转让（目前未实现）
        }

        return true;
    }

    /**
     * 更新转让班级添加密码校验
     *
     * @author dzy
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean transferV2(CourseTransferV2DTO dto) {
        UserPasswordResultDTO transferor = R.as(userClient.findPasswordById(LoginHelper.getUserId()));
        CommonErrors.BAD_REQUEST.check(Md5HashUtil.checkPwd(transferor.getSalt(), dto.getPassword(), transferor.getPassword()), MessageUtils.message("sys.user.password.is.invalid"));

        return this.transfer(courseTransferConvert.convert(dto));
    }

    @Override
    public Integer updateCourseName(Long courseId, String courseName) {
        CourseEntity courseEntity = Optional.ofNullable(courseMapper.selectById(courseId)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("该课程不存在"));
        //课程名称只可以更改20次
        if (!Objects.equals(courseEntity.getCourseName(), courseName)) {
            CommonErrors.BAD_REQUEST.check(courseEntity.getNameEditCount() + 1 <= CourseConstants.Course.EDITABLE_COUNT, "课程名称只可更改20次");
            courseEntity.setNameEditCount(courseEntity.getNameEditCount() + 1);
            courseEntity.setIsNameEditable(YesNoEnum.NO.ordinal());
            courseEntity.setCourseName(courseName);
        }
        return baseMapper.updateById(courseEntity);
    }

    /**
     * 我创建的其他课程
     *
     * @param courseId 课程id
     * @return List<CourseResultDTO>
     */
    @Override
    public List<CourseResultDTO> myCreatedOtherCourse(Long courseId) {
        CourseQueryDTO queryDTO = new CourseQueryDTO();
        queryDTO.setUserId(LoginHelper.getUserId());
        List<CourseResultDTO> courseList = find(queryDTO);

        if (CollectionUtils.isNotEmpty(courseList)) {
            courseList = courseList.stream().filter(course -> !Objects.equals(courseId, course.getId())).collect(Collectors.toList());
        }

        Lambdas.each(courseList, this::setClassList);
        return courseList;
    }

    @Override
    public Long findCourseCountByUserId(Long userId) {
        CourseQueryDTO queryDTO = new CourseQueryDTO();
        queryDTO.setUserId(userId);
        return count(wrapper(queryDTO));
    }

    @Override
    public Long copy(CourseResultDTO course, Long userId) {
        CourseEntity entity = courseConvert.copy(course);
        entity.setId(null);
        entity.setCreateBy(userId);
        entity.setUserId(userId);
        entity.setCreateTime(LocalDateTime.now());
        save(entity);
        return entity.getId();
    }

    @Override
    public CourseResultDTO findByClassId(Long classId) {
        return baseMapper.findByClassId(classId);
    }

    private void setClassList(CourseResultDTO courseResultDTO) {
        courseResultDTO.setClassList(classService.findByCourseId(courseResultDTO.getId()));
    }

    private void renderTeacher(ClassResultDTO dto) {
        ClassTeacherResultDTO teacherResult = classService.findTeacherByClassId(dto.getId());
        dto.setTeacher(teacherResult);
    }

    private CourseResultDTO findByClassIdAndLoginId(Long classId, Integer isTeacher) {
        return baseMapper.findByClassIdAndLoginId(classId, LoginHelper.getUserId(), isTeacher);
    }

    @Override
    public Boolean archiveSelf(Long courseId) {
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(courseId), "课程ID不能为空");
        CourseEntity old = Optional.ofNullable(getById(courseId)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("要恢复归档的课程不存在"));

        List<ClassStudentResultDTO> studentList = classStudentService.findByCourseIdAndStudentId(courseId, LoginHelper.getUserId());
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(studentList), "数据不存在");

        //归档学生，老师也是属于班级中的学生
        studentList.forEach(s -> s.setIsArchive(YesNoEnum.YES.ordinal()));

        return classStudentService.saveBatch(studentList.stream().map(classStudentConvert::convertEntity).collect(Collectors.toList()));
    }

    /**
     * 置顶课程,置顶-置顶某个学生的某个课程置顶，is_top字段在学生表
     *
     * @param courseId 课程id
     * @return Boolean
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean up(Long courseId) {
        CourseEntity old = Optional.ofNullable(getById(courseId)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("数据不存在"));
        return this.upAndDownExecute(courseId, YesNoEnum.YES.ordinal());
    }

    /**
     * 取消置顶课程,取消置顶-取消置顶某个学生的某个课程置顶，is_top字段在学生表
     *
     * @param courseId 课程id
     * @return Boolean
     */
    @Override
    public Boolean down(Long courseId) {
        CourseEntity old = Optional.ofNullable(getById(courseId)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("数据不存在"));
        return this.upAndDownExecute(courseId, YesNoEnum.NO.ordinal());
    }

    @Override
    public Boolean myJoinUp(Long courseId) {
        List<ClassRoleEnum> roles = new ArrayList<>();
        roles.add(ClassRoleEnum.REPRESENTATIVE);
        roles.add(ClassRoleEnum.STUDENT);
        List<ClassStudentResultDTO> studentList = classStudentService.findByCourseIdAndStudentIdAndRoles(courseId, LoginHelper.getUserId(), roles);
        return execute(studentList, YesNoEnum.YES.ordinal());
    }

    @Override
    public Boolean myCreateUp(Long courseId) {
        List<ClassRoleEnum> roles = new ArrayList<>();
        roles.add(ClassRoleEnum.TEACHER);
        roles.add(ClassRoleEnum.ASSISTANT);
        List<ClassStudentResultDTO> studentList = classStudentService.findByCourseIdAndStudentIdAndRoles(courseId, LoginHelper.getUserId(), roles);
        return execute(studentList, YesNoEnum.YES.ordinal());
    }

    @Override
    public Boolean myJoinDown(Long courseId) {
        List<ClassRoleEnum> roles = new ArrayList<>();
        roles.add(ClassRoleEnum.REPRESENTATIVE);
        roles.add(ClassRoleEnum.STUDENT);
        List<ClassStudentResultDTO> studentList = classStudentService.findByCourseIdAndStudentIdAndRoles(courseId, LoginHelper.getUserId(), roles);
        return execute(studentList, YesNoEnum.NO.ordinal());
    }

    @Override
    public Boolean myCreateDown(Long courseId) {
        List<ClassRoleEnum> roles = new ArrayList<>();
        roles.add(ClassRoleEnum.TEACHER);
        roles.add(ClassRoleEnum.ASSISTANT);
        List<ClassStudentResultDTO> studentList = classStudentService.findByCourseIdAndStudentIdAndRoles(courseId, LoginHelper.getUserId(), roles);
        return execute(studentList, YesNoEnum.NO.ordinal());
    }

    @Override
    public Boolean myJoinArchiveSelf(Long courseId) {
        List<ClassRoleEnum> roles = new ArrayList<>();
        roles.add(ClassRoleEnum.REPRESENTATIVE);
        roles.add(ClassRoleEnum.STUDENT);
        List<ClassStudentResultDTO> studentList = classStudentService.findByCourseIdAndStudentIdAndRoles(courseId, LoginHelper.getUserId(), roles);

        studentList.forEach(s -> s.setIsArchive(YesNoEnum.YES.ordinal()));
        return classStudentService.saveBatch(studentList.stream().map(classStudentConvert::convertEntity).collect(Collectors.toList()));
    }

    @Override
    public Boolean myCreateArchiveSelf(Long courseId) {
        List<ClassRoleEnum> roles = new ArrayList<>();
        roles.add(ClassRoleEnum.TEACHER);
        roles.add(ClassRoleEnum.ASSISTANT);
        List<ClassStudentResultDTO> studentList = classStudentService.findByCourseIdAndStudentIdAndRoles(courseId, LoginHelper.getUserId(), roles);

        studentList.forEach(s -> s.setIsArchive(YesNoEnum.YES.ordinal()));
        return classStudentService.saveBatch(studentList.stream().map(classStudentConvert::convertEntity).collect(Collectors.toList()));

    }

    @Override
    public Boolean myJoinRecoverSelf(Long courseId) {
        CourseEntity old = Optional.ofNullable(getById(courseId)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("数据不存在"));
        CommonErrors.BAD_REQUEST.check(!Objects.equals(CourseArchiveStatusEnum.ARCHIVE_ALL, old.getArchive()), "老师已归档全班，不可恢复");

        List<ClassRoleEnum> roles = new ArrayList<>();
        roles.add(ClassRoleEnum.REPRESENTATIVE);
        roles.add(ClassRoleEnum.STUDENT);
        List<ClassStudentResultDTO> studentList = classStudentService.findByCourseIdAndStudentIdAndRoles(courseId, LoginHelper.getUserId(), roles);
        studentList.forEach(s -> {
            s.setIsArchive(YesNoEnum.NO.ordinal());
            s.setIsTop(YesNoEnum.NO.ordinal());
        });

        return classStudentService.saveBatch(studentList.stream().map(classStudentConvert::convertEntity).collect(Collectors.toList()));
    }

    @Override
    public Boolean myCreateRecoverSelf(Long courseId) {
        CourseEntity old = Optional.ofNullable(getById(courseId)).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("数据不存在"));
        CommonErrors.BAD_REQUEST.check(!Objects.equals(CourseArchiveStatusEnum.ARCHIVE_ALL, old.getArchive()), "老师已归档全班，不可恢复");
        List<ClassRoleEnum> roles = new ArrayList<>();
        roles.add(ClassRoleEnum.TEACHER);
        roles.add(ClassRoleEnum.ASSISTANT);
        List<ClassStudentResultDTO> studentList = classStudentService.findByCourseIdAndStudentIdAndRoles(courseId, LoginHelper.getUserId(), roles);
        studentList.forEach(s -> {
            s.setIsArchive(YesNoEnum.NO.ordinal());
            s.setIsTop(YesNoEnum.NO.ordinal());
        });

        return classStudentService.saveBatch(studentList.stream().map(classStudentConvert::convertEntity).collect(Collectors.toList()));
    }

    /**
     * 置顶只针对个人，但是一个课程下的多个班级中有可能出现重复的学生
     *
     * @param courseId 课程id
     * @param status   置顶/取消置顶
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean upAndDownExecute(Long courseId, Integer status) {
        List<ClassStudentResultDTO> studentList = classStudentService.findByCourseIdAndStudentId(courseId, LoginHelper.getUserId());
        return execute(studentList, status);
    }

    /**
     * 置顶只针对个人，但是一个课程下的多个班级中有可能出现重复的学生
     *
     * @param studentList 学生列表
     * @param status      置顶/取消置顶
     * @return boolean
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean execute(List<ClassStudentResultDTO> studentList, Integer status) {
        CommonErrors.BAD_REQUEST.check(CollectionUtils.isNotEmpty(studentList), "学生数据不存在");
        List<ClassStudentEntity> updateList = new ArrayList<>();
        //置顶（取消置顶）学生
        studentList.forEach(s -> {
            ClassStudentEntity entity = classStudentConvert.convertEntity(s);
            entity.setIsTop(status);
            entity.setToppingTime(LocalDateTime.now());
            updateList.add(entity);
        });
        classStudentService.saveBatch(updateList);
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    public void saveCloudTextbook(CourseEntity entity, List<Long> classIdList, CourseUpdateDTO dto) {
        if (Objects.isNull(entity.getCloudTextbookId()) && Objects.nonNull(dto.getCloudTextbookId())) {
            copyCourseContentService.saveCloudTextbook(entity.getId(), classIdList, dto.getCloudTextbookId());
        }
    }

    private void setCloudTextbookAttribute(Long textbookId, CourseEntity entity) {
        CloudTextbookInfoResultDTO textbookInfoResultDTO = R.as(cloudTextbookInfoClient.findById(textbookId));
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(textbookInfoResultDTO), "云课本不存在");
        entity.setCloudTextbookName(textbookInfoResultDTO.getName());
        entity.setCloudTextbookCoverImage(textbookInfoResultDTO.getCoverImage());
    }
}
